#include "commom_ipc.h"

#ifdef WRITE_ERR_LOG
FILE *pLog;
#endif

MallocMem::MallocMem(int byte)
{
    pBuffer = new char[byte];
    length = byte;
}

char *MallocMem::c_str()
{
    return pBuffer;
}

int MallocMem::set_buffer(char * pdata)
{
    memset(pBuffer, 0, length);
    memcpy(pBuffer, pdata, length);

    return SUCCESS;
}

int MallocMem::get_buffer(char * pdata)
{
    memcpy(pdata, pBuffer, length);
    memset(pBuffer, 0, length);

    return SUCCESS;
}


int MallocMem::size()
{
    return length;
}

MallocMem::~MallocMem()
{
    delete[] pBuffer;
}

MessageData::MessageData()
{
    m_type = 0;
    valid_size = 0;
    cmd = 0;
    memset(m_buffer, 0, sizeof(m_buffer));
}

int MessageData::ImportBuffer(char *pdata)
{
    memset(m_buffer, 0, sizeof(m_buffer));
    memcpy(m_buffer, pdata, sizeof(m_buffer));

    return SUCCESS;
}

int MessageData::ExportBuffer(char *pdata)
{
    memcpy(pdata, m_buffer, sizeof(m_buffer));
    memset(m_buffer, 0, sizeof(m_buffer));

    return SUCCESS;
}


Message::Message(key_t key)
{
    msg_id = msgget(key, 0666|IPC_CREAT);
    if (msg_id == -1)
    {
        err_printf("create_msg failed!\n");
    }
}

int Message::send_msg()
{
    if (msgsnd(msg_id, &msg, sizeof(msg)-sizeof(long int), 0) == -1)
    {
        err_printf("send_msg failed!\n");
    }
    memset(&msg, 0, sizeof(msg));

    return SUCCESS;
}

int Message::recv_msg(long int type)
{
    memset(&msg, 0, sizeof(msg));
    if (msgrcv(msg_id, &msg, sizeof(msg)-sizeof(long int), type, 0)== -1)
    {
        err_printf("msg_rev failed!\n");
    }
    return SUCCESS;
}

int Message::set_message(MessageData *message)
{
    memset(&msg, 0, sizeof(msg));
    memcpy(&msg, message, sizeof(msg));

    return SUCCESS;
}

int Message::get_message(MessageData *message)
{
    memcpy(message, &msg, sizeof(msg));
    memset(&msg, 0, sizeof(msg));

    return SUCCESS;
}

int Message::delete_msg()
{
    if (msgctl(msg_id, IPC_RMID, NULL)==-1)
    {
        err_printf("msg_del failed!\n");
    }

    return SUCCESS;
}

ShareMemoryData::ShareMemoryData()
{
    valid_size = 0;
    memset(buffer, 0, sizeof(buffer));
}

ShareMemory::ShareMemory(key_t key)
{
    shm_id = shmget(key, sizeof(ShareMemoryData), 0666|IPC_CREAT);
    if (shm_id == -1)
    {
        err_printf("shmget failed!\n");
    }

    shm = (ShareMemoryData*)shmat(shm_id, NULL, 0);
    if ((int)shm == -1)
    {
        err_printf("shmat failed!\n");
    }
}

int ShareMemory::get_shm(ShareMemoryData *shmptr)
{
    memcpy(shmptr, shm, sizeof(ShareMemoryData));
    memset(shm, 0, sizeof(ShareMemoryData));

    return SUCCESS;
}

int ShareMemory::set_shm(ShareMemoryData *shmptr)
{
    memset(shm, 0, sizeof(ShareMemoryData));
    memcpy(shm, shmptr, sizeof(ShareMemoryData));

    return SUCCESS;
}

int ShareMemory::delete_shm()
{
    shmdt(shm);
    shmctl(shm_id, IPC_RMID, NULL);
    return SUCCESS;
}

/*
* @brief:Create Semaphore
* @param key: key value
* @param num_sems: Semaphore numbers you want to create                       
* @retval: No                            
*/

Semaphore::Semaphore(key_t key, int num_sems)
{
    sem_set_id = semget(key, num_sems, 0666|IPC_CREAT);
    if (sem_set_id == -1)
    {
        err_printf("semget failed!\n");
    }
}

/*
* @brief:Init Semaphore
* @param index: the index of Semaphore you create
* @param value: Semaphore init value       
* @retval: 0                            
*/

int Semaphore::InitSem(int index,int value)
{
    sem.val = value;
    if (semctl(sem_set_id, index, SETVAL, sem)==-1)
    {
        err_printf("set_sem failed!\n");
    }
    return SUCCESS;
}

int Semaphore::WaitSem(int index)
{
    struct sembuf sem_buf;
    sem_buf.sem_num = index;
    sem_buf.sem_op = -1;
    sem_buf.sem_flg = SEM_UNDO;
    if(semop(sem_set_id,&sem_buf,1) == -1)
    {
        err_printf("WaitSem failed");
    }

    return SUCCESS;
}

int Semaphore::SignalSem(int index)
{
    struct sembuf sem_buf;
    sem_buf.sem_num = index;
    sem_buf.sem_op = 1;
    sem_buf.sem_flg = SEM_UNDO;
    if(semop(sem_set_id,&sem_buf,1) == -1)
    {
        err_printf("SignalSem failed");
    }

    return SUCCESS;
}


int Semaphore::DeleteSem(int index)
{
    if (semctl(sem_set_id, index, IPC_RMID, sem)==-1)
    {
        err_printf("deletet sem failed!\n");
    }

    return SUCCESS;
}

SocketDataHead::SocketDataHead()
{
    cmd = 0;
    valid_size= 0;
}


SocketData::SocketData()
{
    memset(buffer, 0, sizeof(buffer));
}

int SocketData::set_cmd(int cmd)
{
    data_head.cmd = cmd;
    return SUCCESS;
}

int SocketData::get_cmd()
{
    return data_head.cmd;
}

int SocketData::get_valid_size()
{
    return data_head.valid_size;
}

int SocketData::set_valid_size(int valid_size)
{
    data_head.valid_size = valid_size;
    return SUCCESS;
}

int SocketData::ImportBuffer(char *pdata)
{
    memset(buffer, 0, sizeof(buffer));
    memcpy(buffer, pdata, sizeof(buffer));

    return SUCCESS;
}

int SocketData::ExportBuffer(char *pdata)
{
    memcpy(pdata, buffer, sizeof(buffer));
    memset(buffer, 0, sizeof(buffer));

    return SUCCESS;
}

int SocketSession::CommomSessionInit(int domain,int type,int backlog)
{
    sock_type = type;
    sock_domain = domain;
    new_fd = 0;
    sock_backlog = backlog;
    sock_port = 0;
    sock_path = NULL;
    sock_ip = NULL;
    sock_path_size = 0;
    memset(&RemoteAddr, 0, sizeof(RemoteAddr));
    memset(&LocalAddr, 0, sizeof(LocalAddr));
    memset(&client_addr, 0, sizeof(client_addr));
    memset(&server_addr, 0, sizeof(server_addr));

    listen_fd = socket(domain, type, 0);
    if (listen_fd == -1)
    {
        err_printf("create_socket failed!\n");
    }

    if (domain != AF_UNIX)
    {
        int opt = 1;
        if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
        {
            err_printf("setsockopt failed!\n");
        }
    }
    return SUCCESS;
}
/*
* @brief:Create AF_INET socket session for TCP or UDP
* @param ipaddr: if the session is server, ipaddr should be set NULL
                          if the session is client, ipaddr should be set specific address you want to connect
* @param port: the specific port you want to bind
* @param domain: the protocol, example AF_INET
* @param type: the protocol type, example SOCK_STREAM or SOCK_DGRAM
* @param backlog: Maximum length of queue waiting for connection
               Note:     if the session is TCP server, the value should be 
                            set correctly. Other conditions, the value should be set 0
* @retval: No                            
*/

SocketSession::SocketSession(char *ipaddr, int port, int domain, int type, int backlog)
{
    CommomSessionInit(domain, type, backlog);

    sock_port = port;
    if (ipaddr != NULL)
    {
        sock_ip = new char[16];
        memcpy(sock_ip, ipaddr, 16);
    }
}

/*
* @brief:Create AF_UNIX socket session for TCP or UDP

* @param domain: the protocol, example AF_UNIX
* @param type: the protocol type, example SOCK_STREAM or SOCK_DGRAM
* @param backlog: Maximum length of queue waiting for connection
           Must Note: if the session is TCP server, the value should be 
                            set correctly. if the session is UDP server, the 
                            value should be set 1. Other conditions, the value should be set 0
* @param path: if the session is server, path you want to create should be set correctly
                       if the session is client, path should be set specific address you want to connect
* @param path_size: the path size
* @retval: No                            
*/

SocketSession::SocketSession(int domain, int type, int backlog, char *path, int path_size)
{
    CommomSessionInit(domain, type, backlog);

    sock_path = new char[path_size];
    memcpy(sock_path, path, path_size);
    sock_path_size = path_size;
}

SocketSession::~SocketSession()
{
    if (sock_path != NULL)
    {
        delete[] sock_path;
    }
    if (sock_ip != NULL)
    {
        delete[] sock_ip;
    }
}


int SocketSession::SetSocketData(SocketData *pdata)
{
    memset(&sockdata, 0, sizeof(SocketData));
    memcpy(&sockdata, pdata, sizeof(SocketData));

    return SUCCESS;
}

int SocketSession::GetSocketData(SocketData *pdata)
{
    memcpy(pdata, &sockdata, sizeof(SocketData));
    memset(&sockdata, 0, sizeof(SocketData));

    return SUCCESS;
}

int SocketSession::InitServerSocket()
{
    switch(sock_domain)
    {
    case AF_INET:
        LocalAddr.sin_family = AF_INET;
        LocalAddr.sin_port = htons(sock_port);
        LocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(listen_fd, (struct sockaddr *)&LocalAddr, sizeof(struct sockaddr)) == -1)
        {
            err_printf("sock_bind failed!\n");
        }
        if (sock_type == SOCK_STREAM)
        {
            if (listen(listen_fd, sock_backlog) == -1)
            {
                err_printf("sock_listen failed!\n");
            }
        }
        break;
    case AF_UNIX:
        if (sock_backlog != UNIX_OVER_UDP_CLIENT) //tcp or udp:  sock_backlog not equal to 0
        {
            unlink(sock_path);
            server_addr.sun_family = AF_UNIX;
            strcpy(server_addr.sun_path, sock_path);
            bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
            if (sock_type == SOCK_STREAM)
            {
                listen(listen_fd, sock_backlog);
            }
        }
        else if (sock_backlog == UNIX_OVER_UDP_CLIENT)
        {
            client_addr.sun_family = AF_UNIX;
            char str[] = "_client";
            int length = sizeof(str);
            char sock_path_client[length + sock_path_size];
            sprintf(sock_path_client, "%s%s", sock_path, str);
            unlink(sock_path_client);
            strcpy(client_addr.sun_path, sock_path_client);
            bind(listen_fd, (struct sockaddr *)&client_addr, sizeof(client_addr));
        }
        break;
    default:
        break;
    }
    return SUCCESS;
}

int SocketSession::SetNoBlock()
{
    int flag = fcntl(listen_fd, F_GETFL, 0);
    flag |= O_NONBLOCK;
    if (fcntl(listen_fd, F_SETFL, flag) < 0)
    {
        err_printf("fcntl failed.\n");
    }

    return SUCCESS;
}

int SocketSession::SocketAccept()
{
    socklen_t len = 0;
    switch(sock_domain)
    {
    case AF_INET:
        len = sizeof(struct sockaddr);
        new_fd = accept(listen_fd, (struct sockaddr *)&RemoteAddr, &len);
        if (new_fd == -1)
        {
            err_printf("sock_accept failed!\n");
        }
        printf("new_fd: %d\n", new_fd);
        printf("connected from %s, port: %d\n", inet_ntoa(RemoteAddr.sin_addr), ntohs(RemoteAddr.sin_port));
        break;
    case AF_UNIX:
        len = sizeof(client_addr);
        new_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &len);
        printf("connection from %s\n", client_addr.sun_path);
        break;
    default:break;    
    }
    
    return SUCCESS;
}

int SocketSession::SocketConnect()
{
    struct sockaddr_in remote_addr;
    int ret = 0;
    switch(sock_domain)
    {
    case AF_INET:     
        memset((char *)&remote_addr, 0, sizeof(struct sockaddr_in));
        remote_addr.sin_family = AF_INET;
        remote_addr.sin_port = htons(sock_port);
        remote_addr.sin_addr.s_addr = inet_addr(sock_ip);
        if (connect(listen_fd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))==-1)
        {
            err_printf("sock_connect failed!\n");
        }
        break;
    case AF_UNIX:
        client_addr.sun_family = AF_UNIX;
        strcpy(client_addr.sun_path, sock_path);
        ret = connect(listen_fd, (struct sockaddr *)&client_addr, sizeof(client_addr));
        if(ret == -1)
        {
            err_printf("connect failed: ");
        }
        break;
    default:break;    
    }
    

    return SUCCESS;
}

int SocketSession::CommomOverTCPSend(int sockfd)
{
    int bytes;
    bytes = send(sockfd, &sockdata.data_head, sizeof(sockdata.data_head), 0);
    if (bytes == -1)
    {
        err_printf("send data head failed!\n");
    }

    int nbytes = 0;
    char *Addr = sockdata.buffer;
    int sent_size = 0;
    int each_send_size = MAX_SOCKET_EACH_SEND_SIZE;
    int remain_send_size = sockdata.data_head.valid_size;
    while(sent_size != sockdata.data_head.valid_size)
    {
        if (remain_send_size < MAX_SOCKET_EACH_SEND_SIZE)
        {
            each_send_size = remain_send_size;
        }
        nbytes = send(sockfd, Addr, each_send_size, 0);
        if (nbytes == -1)
        {
            err_printf("sock send data failed!\n");
            break;
        }
        sent_size += nbytes;
        Addr += nbytes;
        remain_send_size -= nbytes;
    }
    memset(&sockdata, 0, sizeof(SocketData));

    return bytes + sent_size;
}

int SocketSession::CommomOverTCPRecv(int sockfd)
{
    memset(&sockdata, 0, sizeof(SocketData));
    int bytes = 0;
    bytes = recv(sockfd, &sockdata.data_head, sizeof(sockdata.data_head), 0);
    if (bytes == -1)
    {
        err_printf("recv data_head failed!\n");
    }

    int nbytes = 0;
    char *Addr = sockdata.buffer;
    int received_size = 0;
    int each_recv_size = MAX_SOCKET_EACH_SEND_SIZE;
    int remain_recv_size = sockdata.data_head.valid_size;

    while(received_size != sockdata.data_head.valid_size)
    {
        if (remain_recv_size < MAX_SOCKET_EACH_SEND_SIZE)
        {
            each_recv_size = remain_recv_size;
        }
        nbytes = recv(sockfd, Addr, each_recv_size, 0);
        if (nbytes == -1)
        {
            err_printf("sock recv data failed!\n");
            break;
        }
        received_size += nbytes;
        Addr += nbytes;
        remain_recv_size -= nbytes;
    }

    return bytes + received_size;
}

int SocketSession::SocketSend()
{
    int bytes = 0;
    switch(sock_domain)
    {
    case AF_INET:
        switch(sock_type)
        {
        case SOCK_STREAM:
            if (new_fd != 0)
            {
                bytes = CommomOverTCPSend(new_fd);
            }
            else if (new_fd == 0)
            {
                bytes = CommomOverTCPSend(listen_fd);
            }
            break;
        case SOCK_DGRAM:
            if (sock_ip == NULL)
            {
                bytes = CommomOverUDPSend(&RemoteAddr, NULL, 0);
            }
            else if (sock_ip != NULL)
            {
                bytes = CommomOverUDPSend(&RemoteAddr, sock_ip, sock_port);
            }
            break;
        default:break;    
        }
        break;
    case AF_UNIX:
        switch(sock_type)
        {
        case SOCK_STREAM:
            if (new_fd != 0)
            {
                bytes = CommomOverTCPSend(new_fd);
            }
            else if (new_fd == 0)
            {
                bytes = CommomOverTCPSend(listen_fd);
            }
            break;
        case SOCK_DGRAM:
            if (sock_backlog == UNIX_OVER_UDP_SERVER)
            {
                bytes = CommomOverUDPSend((struct sockaddr_in *)&client_addr, NULL, 0);
            }
            else if (sock_backlog == UNIX_OVER_UDP_CLIENT)
            {
                bytes = CommomOverUDPSend((struct sockaddr_in *)&server_addr, NULL, 0);
            }
            break;
        default:break;    
        }
        break;
    default:break;    
    }

    return bytes;
}


int SocketSession::SocketRecv()
{
    int bytes = 0;
    switch(sock_domain)
    {
    case AF_INET:
        switch(sock_type)
        {
        case SOCK_STREAM:
            if (new_fd != 0)
            {
                bytes = CommomOverTCPRecv(new_fd);
            }
            else if (new_fd == 0)
            {
                bytes = CommomOverTCPRecv(listen_fd);
            }
            break;
        case SOCK_DGRAM:
            if (sock_ip == NULL)
            {
                bytes = CommomOverUDPRecv(&RemoteAddr);
                printf("connected from %s, port: %d\n", inet_ntoa(RemoteAddr.sin_addr), ntohs(RemoteAddr.sin_port));                
            }
            else if (sock_ip != NULL)
            {
                bytes = CommomOverUDPRecv(&LocalAddr);
            }
            break;
        default:break;       
        }
        break;
    case AF_UNIX:
        switch(sock_type)
        {
        case SOCK_STREAM:
            if (new_fd != 0)
            {
                bytes = CommomOverTCPRecv(new_fd);
            }
            else if (new_fd == 0)
            {
                bytes = CommomOverTCPRecv(listen_fd);
            }
            break;
        case SOCK_DGRAM:
            if (sock_backlog == UNIX_OVER_UDP_SERVER)
            {
                bytes = CommomOverUDPRecv((struct sockaddr_in *)&client_addr);
                printf("connection from %s\n", client_addr.sun_path);
            }
            else if (sock_backlog == UNIX_OVER_UDP_CLIENT)
            {
                bytes = CommomOverUDPRecv((struct sockaddr_in *)&client_addr);
            }
            break;
        default:break;       
        }
        break;
    default:break;       
    }

    return bytes;
}


int SocketSession::CommomOverUDPSend(struct sockaddr_in *sock_addr,char * ipaddr,int port)
{
    if (ipaddr != NULL && port != 0)
    {
        sock_addr->sin_family = AF_INET;
        sock_addr->sin_port = htons(port);

        if (inet_pton(AF_INET, ipaddr, &sock_addr->sin_addr) < 0)
        {
            err_printf("inet_pton failed.\n");
        }
    }
    socklen_t len = 0;
    if (sock_domain == AF_INET)
    {
         len = sizeof(struct sockaddr_in);
    }else if (sock_domain == AF_UNIX)
    {
        len = sizeof(struct sockaddr_un);
        if (sock_backlog == UNIX_OVER_UDP_CLIENT)
        {
            struct sockaddr_un *temp = (struct sockaddr_un *)sock_addr;
            temp->sun_family = AF_UNIX;
            strcpy(temp->sun_path, sock_path);
        }     
    }
    int bytes;
    bytes = sendto(listen_fd, &sockdata.data_head, sizeof(sockdata.data_head), 0, (struct sockaddr*)sock_addr, len);
    if (bytes == -1)
    {
        err_printf("send data head failed!\n");
    }
    
    
    int nbytes = 0;
    char *Addr = sockdata.buffer;
    int sent_size = 0;
    int each_send_size = MAX_SOCKET_EACH_SEND_SIZE;
    int remain_send_size = sockdata.data_head.valid_size;
    while(sent_size !=  sockdata.data_head.valid_size)
    {
        if (remain_send_size < MAX_SOCKET_EACH_SEND_SIZE)
        {
            each_send_size = remain_send_size;
        }
        nbytes = sendto(listen_fd, Addr, each_send_size, 0, (struct sockaddr*)sock_addr, len);
        if (nbytes == -1)
        {
            err_printf("sock send data failed!\n");
            break;
        }
        sent_size += nbytes;
        Addr += nbytes;
        remain_send_size -= nbytes;
    }
    memset(&sockdata, 0, sizeof(SocketData));

    return bytes + sent_size;
}

int SocketSession::CommomOverUDPRecv(struct sockaddr_in *sock_addr)
{
    memset(&sockdata, 0, sizeof(SocketData));
    int bytes = 0;
    socklen_t len = 0;
    if (sock_domain == AF_INET)
    {
        len = sizeof(struct sockaddr_in);
    }else if (sock_domain == AF_UNIX)
    {
        len = sizeof(struct sockaddr_un);
    }
    
    bytes = recvfrom(listen_fd, &sockdata.data_head, sizeof(sockdata.data_head), 0, (struct sockaddr*)sock_addr, &len);
    if (bytes == -1)
    {
        err_printf("recv data_head failed!\n");
    }

    int nbytes = 0;
    char *Addr = sockdata.buffer;
    int received_size = 0;
    int each_recv_size = MAX_SOCKET_EACH_SEND_SIZE;
    int remain_recv_size = sockdata.data_head.valid_size;

    while(received_size != sockdata.data_head.valid_size)
    {
        if (remain_recv_size < MAX_SOCKET_EACH_SEND_SIZE)
        {
            each_recv_size = remain_recv_size;
        }
        nbytes = recvfrom(listen_fd, Addr, each_recv_size, 0, (struct sockaddr*)sock_addr, &len);
        if (nbytes == -1)
        {
            err_printf("sock recv data failed!\n");
            break;
        }
        received_size += nbytes;
        Addr += nbytes;
        remain_recv_size -= nbytes;
    }

    return bytes + received_size;
}

int SocketSession::SetSocketSendBufSize(int size)
{
    if (setsockopt(listen_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1)
    {
        err_printf("setsockopt sendbufsize fail!\n");
    }

    return SUCCESS;
}

int SocketSession::SetSocketRecvBufSize(int size)
{
    if (setsockopt(listen_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) == -1)
    {
        err_printf("setsockopt sendbufsize fail!\n");
    }

    return SUCCESS;
}


int SocketSession::SocketCloseClient()
{
    close(new_fd);

    return SUCCESS;
}

int SocketSession::SocketCloseListen()
{
    close(listen_fd);

    return SUCCESS;
}


